package com.stripe.android.challenge.confirmation

import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import com.stripe.android.testing.CoroutineTestRule
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import java.io.IOException

@RunWith(RobolectricTestRunner::class)
internal class IntentConfirmationChallengeViewModelTest {

    private val testDispatcher = UnconfinedTestDispatcher()

    @get:Rule
    val coroutineTestRule = CoroutineTestRule(testDispatcher)

    @Test
    fun `when Ready event is received, bridgeReady emits value`() = runTest {
        val fakeBridgeHandler = FakeConfirmationChallengeBridgeHandler()
        val viewModel = createViewModel(fakeBridgeHandler)

        viewModel.bridgeReady.test {
            // Initial state
            expectNoEvents()

            // Emit Ready event
            fakeBridgeHandler.emitEvent(ConfirmationChallengeBridgeEvent.Ready)

            // Should emit value
            awaitItem()

            ensureAllEventsConsumed()
        }
    }

    @Test
    fun `when Success event is received, result emits Success with clientSecret`() = runTest {
        val fakeBridgeHandler = FakeConfirmationChallengeBridgeHandler()
        val viewModel = createViewModel(fakeBridgeHandler)
        val expectedClientSecret = "pi_test_secret_123"

        viewModel.result.test {
            fakeBridgeHandler.emitEvent(
                ConfirmationChallengeBridgeEvent.Success(clientSecret = expectedClientSecret)
            )

            val result = awaitItem()
            assertThat(result).isInstanceOf(IntentConfirmationChallengeActivityResult.Success::class.java)
            val successResult = result as IntentConfirmationChallengeActivityResult.Success
            assertThat(successResult.clientSecret).isEqualTo(expectedClientSecret)

            ensureAllEventsConsumed()
        }
    }

    @Test
    fun `when Error event is received, result emits Failed with error`() = runTest {
        val fakeBridgeHandler = FakeConfirmationChallengeBridgeHandler()
        val viewModel = createViewModel(fakeBridgeHandler)
        val expectedError = IOException("Network error")

        viewModel.result.test {
            fakeBridgeHandler.emitEvent(
                ConfirmationChallengeBridgeEvent.Error(cause = expectedError)
            )

            val result = awaitItem()
            assertThat(result).isInstanceOf(IntentConfirmationChallengeActivityResult.Failed::class.java)
            val failedResult = result as IntentConfirmationChallengeActivityResult.Failed
            assertThat(failedResult.error).isEqualTo(expectedError)

            ensureAllEventsConsumed()
        }
    }

    @Test
    fun `when handleWebViewError is called, result emits Failed with WebViewError`() = runTest {
        val fakeBridgeHandler = FakeConfirmationChallengeBridgeHandler()
        val viewModel = createViewModel(fakeBridgeHandler)
        val webViewError = WebViewError(
            message = "net::ERR_FAILED",
            url = "https://example.com/payment",
            errorCode = -2,
            webViewErrorType = "generic_resource_error"
        )

        viewModel.result.test {
            viewModel.handleWebViewError(webViewError)

            val result = awaitItem()
            assertThat(result).isInstanceOf(IntentConfirmationChallengeActivityResult.Failed::class.java)
            val failedResult = result as IntentConfirmationChallengeActivityResult.Failed
            assertThat(failedResult.error).isEqualTo(webViewError)
            assertThat(failedResult.error).isInstanceOf(WebViewError::class.java)

            ensureAllEventsConsumed()
        }
    }

    private fun createViewModel(
        bridgeHandler: ConfirmationChallengeBridgeHandler
    ) = IntentConfirmationChallengeViewModel(
        bridgeHandler = bridgeHandler,
        workContext = testDispatcher
    )
}
